
#include "OS_Synchronizers.h"


//**************************** OS_InitSemaphore *******************************
//	Initializes a semaphore. The semaphore can be either counting or binary.
//! \param *semaPt This is a pointer to a semaphore object to initialize
//!
//! \param t This should be either COUNTING or BINARY.
//!
//! \param maxNumThreads This is the maximum number of threads that are allowed
//! inside the semaphore at a given time. For binary semaphores, this should be
//! set to 1
//!
//!	\return	None
//*****************************************************************************
void
OS_InitSemaphore(_tSemaphore *semaPt, TYPE t, unsigned int maxNumThreads){
	semaPt->value = 0;
	semaPt->type = t;
	semaPt->maxNumThreads = maxNumThreads;
	semaPt->owner = -1;
} 

//********************************* OS_Wait ***********************************
//	Spins blocks on a semaphore. The semaphore must be a counting semaphore.
//! \param *semaPt This is a pointer to a semaphore object to wait on
//!
//! \param threadID is the ID of the thread that is waiting on the semaphore.
//! This ensures that an owner of a semaphore is allowed to enter without
//! blocking.
//!
//!	\return	Returns 0 on success. Returns -1 otherwise
//*****************************************************************************
int
OS_Wait(_tSemaphore *semaPt){
	if(semaPt->type != COUNTING){
		return -1; //Must be counting semaphore
	}
	while(1){
		IntMasterDisable();
	   	if(semaPt->value < semaPt->maxNumThreads){
			semaPt->value++;
			IntMasterEnable();
			break;
		}
		IntMasterEnable();
	}
	return 0;
}  

//******************************** OS_Signal **********************************
//	Releases a counting semaphore. The semaphore must be a counting semaphore.
//! \param *semaPt This is a pointer to a semaphore object to wait on
//!
//!	\return	Returns 0 on success. Returns -1 otherwise
//*****************************************************************************
int
OS_Signal(_tSemaphore *semaPt){
	if(semaPt->type != COUNTING){
		return -1;
	}
	semaPt->value--;
	return 0;
} 

//******************************** OS_bWait ***********************************
//	Spins blocks on a binary semaphore. The semaphore must be a binary semaphore.
//! \param *semaPt This is a pointer to a semaphore object to wait on
//!
//! \param threadID This paramater is optional. If used, it allows for a thread
//! to re-enter a semaphore it already owns without causing deadlock.
//! To use - pass the thread ID of the waiting thread as one of the paramaters.
//! This assumes each thread has a unique thread ID.
//! To ignore pass -1
//!
//!	\return	Returns 0 on success. Returns -1 otherwise
//*****************************************************************************
int
OS_bWait(_tSemaphore *semaPt, unsigned int threadID){
	if(semaPt->type != BINARY){
		return -1;
	}
	
	while(1){
		IntMasterDisable();
		
		if(semaPt->owner != -1 && semaPt->owner == threadID && semaPt->value == 1){
			IntMasterEnable();
			return 0;//Owners are allowed to re-enter the semaphore without blocking
		}
	   	if(semaPt->value == 0){
			semaPt->value = 1;
			semaPt->owner = threadID;
			IntMasterEnable();
			break;
		}
		IntMasterEnable();
	}
	return 0;
}  

//******************************* OS_bSignal **********************************
//	Releases a binary semaphore. The semaphore must be a binary semaphore.
//! \param *semaPt This is a pointer to a semaphore object to release
//!
//! \param threadID This paramater is optional. If used, it provides protection
//! against non-owning threads releasing access to the semaphore
//! To use - pass the thread ID of the signaling thread as one of the paramaters.
//! This assumes each thread has a unique thread ID.
//! To ignore pass -1
//!
//!	\return	Returns 0 on success. Returns -1 otherwise
//*****************************************************************************
int
OS_bSignal(_tSemaphore *semaPt, int threadID){
	if(semaPt->type != BINARY) return -1;
	if(semaPt->owner == threadID || threadID == -1){
		semaPt->owner = -1;
		semaPt->value = 0;
	}
	return 0;
}

//************************** OS_InitCriticalSection ***************************
//	Initializes a lampart critical section. Unlike a semaphore, a lampart
//	critical section guaruntees fairness. For example, if thread 1 is inside
//	the critical section and thread 2 requests access, thread 2 is garunteed
//	access once thread 1 exits. Thread 1 cannot reclaim access to the critical
//	section before thread 2. An additional advantage is the lack of disabling
//	interrupts. Lampart critical sections can only be accessed by one thread at
//	a time. There is no counting semaphore equivalent.
//
//! \param lampartCS A pointer to a _tCriticalSection object.
//!
//! \param numberOfThreads This is the maximum number of threads that can
//! possibly pend on this critical section at a given time. This should be the
//! the maximum number of threads supported by the operating system.
//!
//!	\return	Returns 0 on success. Returns -1 otherwise
//*****************************************************************************
int
OS_InitCriticalSection(_tCriticalSection *lampartCS, unsigned int numberOfThreads){
	lampartCS->ThreadWaiting = (volatile unsigned int*)calloc(numberOfThreads,sizeof(int));
	lampartCS->QueueNumber = (volatile unsigned int*)calloc(numberOfThreads,sizeof(int));

	if(lampartCS->ThreadWaiting == NULL || lampartCS->QueueNumber == NULL)
		return -1;

	lampartCS->NumberOfThreads = numberOfThreads;
	
	return 0;
}
	
//************************* OS_EnterCriticalSection **************************
//	Pends on access to a lampart critical section.
//
//! \param ThreadID Thread ID of the thread attempting to access the critical
//! section. Thread IDs must be positive, unique, and cannot exceed the
//! maximum number of threads in the system. I.e., an operating system 
//! supporting 32 threads should not have a thread with ID 32 or higher
//! attempting to access this critical section. This means that the OS must
//! re-use thread IDs from dead threads for new threads.
//!
//!	\return	Returns 0 on success. Returns -1 otherwise
//
// NOTE! Calling this function from within the critical section is safe and 
// will have no effect. I.e. Owning threads can re-enter the critical section
// without blocking
//*****************************************************************************
void OS_EnterCriticalSection(_tCriticalSection *lampartCS, unsigned int ThreadID){
	int j;
	lampartCS->ThreadWaiting[ThreadID] = 1;
	for (j = 0; j < lampartCS->NumberOfThreads; j++){
		if (lampartCS->QueueNumber[j] > lampartCS->QueueNumber[ThreadID])
			lampartCS->QueueNumber[ThreadID] = lampartCS->QueueNumber[j];
	}
	lampartCS->QueueNumber[ThreadID]++;
	lampartCS->ThreadWaiting[ThreadID] = 0;

	for (j = 0; j < lampartCS->NumberOfThreads; j++) {
		while (lampartCS->ThreadWaiting[j]>0){;} 
		while ((lampartCS->QueueNumber[j]!= 0) &&
			(((lampartCS->QueueNumber[j]) < (lampartCS->QueueNumber[ThreadID])) ||
			(((lampartCS->QueueNumber[j]) == (lampartCS->QueueNumber[ThreadID])) && j < ThreadID)))
			{;}
	}
}

//************************* OS_LeaveCriticalSection **************************
//	Pends on access to a lampart critical section.
//
//! \param ThreadID Thread ID of the thread leaving the critical section.
//! Thread IDs must be positive, unique, and cannot exceed the maximum number 
//! of threads in the system. I.e., an operating system supporting 32 threads 
//! should not have a thread with ID 32 or higher attempting to release this 
//! critical section. This means that the OS must re-use thread IDs from dead 
//! threads for new threads.
//!
//!	\return	Returns 0 on success. Returns -1 otherwise
//
//	NOTE! Calls to this function from outside the critical section are safe
//  and will have no effect. I.e. Non owning threads cannot free the critical
//  section.
//*****************************************************************************
void OS_LeaveCriticalSection(_tCriticalSection *lampartCS, unsigned int ThreadID) {
	lampartCS->QueueNumber[ThreadID] = 0;
}
